package backgammon;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.Serializable;
import java.util.ArrayList;
import javax.swing.JPanel;

/**
 * <p>Represents a Backgammon board that contains 28 positions.</p>
 * @author Jacob Kwitkoski
 * @version 1.0
 */
public class Board extends JPanel implements MouseListener,
                                             MouseMotionListener,
                                             Serializable {
    /**
     * <p>The color of the board.</p>
     */
    private final Color boardColor = new Color(60, 100, 35);

    /**
     * <p>The color of the local player's pieces.</p>
     */
    public static Color localPieceColor = new Color(225, 226, 227);

    /**
     * <p>The color of the remote player's pieces.</p>
     */
    public static Color remotePieceColor = new Color(40, 40, 40);

    /**
     * <p>The color of the home positions.</p>
     */
    private final Color homeColor = new Color(140, 65, 45);

    /**
     * <p>The color position one.</p>
     */
    private final Color pos1Color = new Color(160, 130, 80);

    /**
     * <p>The color positions two.</p>
     */
    private final Color pos2Color = new Color(140, 42, 45);

    /**
     * <p>Board array position of the remote player's bar.</p>
     */
    public static final int BAR_POS_R = 1;

    /**
     * <p>Board array position of the local player's bar.</p>
     */
    public static final int BAR_POS_L = 26;

    /**
     * <p>Board array position of the remote player's home row.</p>
     */
    public static final int HOME_POS_R = 27;

    /**
     * <p>Board array position of the local player's home row.</p>
     */
    public static final int HOME_POS_L = 0;

    /**
     * <p>Maximum pieces per position.</p>
     */
    private static final int PIECES_PER_POS = 15;

    /**
     * <p>Board array position of the local player's 5 pieces.</p>
     */
    private static final int LOCAL_5 = 14;

    /**
     * <p>Array position of the local player's 5 pieces in the home area.</p>
     */
    private static final int LOCAL_5_HOME = 7;

    /**
     * <p>Array position of the local player's 3 pieces.</p>
     */
    private static final int LOCAL_3 = 9;

    /**
     * <p>Array position of the local player's 2 pieces.</p>
     */
    private static final int LOCAL_2 = 25;

    /**
     * <p>Array position of the remote player's 5 pieces.</p>
     */
    private static final int REMOTE_5 = 13;

    /**
     * <p>Array position of the remote player's 5 pieces in the home area.</p>
     */
    private static final int REMOTE_5_HOME = 20;

    /**
     * <p>Array position of the remote player's 3 pieces.</p>
     */
    private static final int REMOTE_3 = 18;

    /**
     * <p>Array position of the remote player's 2 pieces.</p>
     */
    private static final int REMOTE_2 = 2;

    /**
     * <p>The number of positions on the board.</p>
     */
    private static final int NUM_OF_POSITIONS = 28;

    /**
     * <p>Array of board positions.</p>
     */
    private Position[] positions;

    /**
     * <p>The currently selected piece.</p>
     */
    private Piece selectedPiece;

    /**
     * <p>The position index that the mouse is on.</p>
     */
    private String positionIndex = "Position index:";

    private GameLogic logic;

    /**
     * <p>Initializes the Backgammon board.</p>
     */
    public Board(GameLogic newLogic) {
        logic = newLogic;
        positions = new Position[NUM_OF_POSITIONS];
        initPositions(); //create the positions on the board
        addPolygonsToPositions(); // add polygons to the positions so they can draw themselves
        setupPieces(); // initial board setup of pieces
        setSize(700, 528);
        addMouseListener(this);
        addMouseMotionListener(this);
    }

    @Override
    public final void paintComponent(final Graphics g) {
        super.paintComponent(g);
        this.setBackground(boardColor);
        for (Position pos : positions) {
            pos.drawPosition(g);
            pos.drawPieces(g);
        }
        if (selectedPiece != null) {
            g.setColor(selectedPiece.getColor());
            Point p = selectedPiece.getPoint();
            g.fillOval(p.x - 23, p.y - 23, 42, 42);
        }
        g.setColor(Color.BLACK);
        g.drawString(positionIndex, 10, 230);
        //g.drawString("Moves: " + positions[positionIndex].moves, 10, 260);
        if (selectedPiece != null) {
            g.drawString("Selected Piece: x = " + selectedPiece.getPoint().x
                + " y = " + selectedPiece.getPoint().y, 10, 270);
        } else {
            g.drawString("Selected Piece does not exist!", 10, 270);
        }
    }

    /**
     * <p>Returns the array of positions.</p>
     * @return the array of positions.
     */
    public final Position[] getPositions() {
        return positions;
    }

    /**
     * <p>Sets the array of positions.</p>
     * @param newPositions the new array of positions to be set.
     */
    public final void setPositions(final Position[] newPositions) {
        positions = newPositions;
    }

    public final Piece getSelectedPiece() {
        return selectedPiece;
    }

    public final void setSelectedPiece(final Piece newSelectedPiece) {
        selectedPiece = newSelectedPiece;
    }

    /**
     * <p>Initializes the positions on the board.
     * Sets all even positions to white and odd numbers to black.</p>
     */
    private void initPositions() {
        for (int i = 0; i < positions.length; i++) {
            if (i == BAR_POS_L || i == BAR_POS_R) {
                positions[i] = new BarPosition(homeColor, i);
            } else if (i == HOME_POS_L || i == HOME_POS_R) {
                positions[i] = new HomePosition(homeColor, i);
            } else {
                if (i % 2 == 0) {
                    positions[i] = new BoardPosition(pos1Color, i);
                } else {
                    positions[i] = new BoardPosition(pos2Color, i);
                }
            }
        }
    }

    /**
     * <p>Sets up the pieces on the entire board.</p>
     */
    private void setupPieces() {
        initialSetup();
        //testBlottable();
        //testBarDrawing();
        //testRemoveFromBar();
    }

    private void initialSetup() {
        //start initial board setup
        placePiecesOnPosition(LOCAL_2, 2, localPieceColor);
        placePiecesOnPosition(LOCAL_3, 3, localPieceColor);
        placePiecesOnPosition(LOCAL_5, 5, localPieceColor);
        placePiecesOnPosition(LOCAL_5_HOME, 5, localPieceColor);
        placePiecesOnPosition(REMOTE_2, 2, remotePieceColor);
        placePiecesOnPosition(REMOTE_3, 3, remotePieceColor);
        placePiecesOnPosition(REMOTE_5, 5, remotePieceColor);
        placePiecesOnPosition(REMOTE_5_HOME, 5, remotePieceColor);
        //end initial board setup
    }

    private void testBarDrawing() {
        //test bar and remote position drawing
        //bar positions
        placePiecesOnPosition(BAR_POS_R, 15, remotePieceColor);
        placePiecesOnPosition(BAR_POS_L, 15, localPieceColor);
        //home positions
        placePiecesOnPosition(HOME_POS_L, 15, localPieceColor);
        placePiecesOnPosition(HOME_POS_R, 15, remotePieceColor);
    }

    private void testBlottable() {
        //test blottable code
        placePiecesOnPosition(LOCAL_2, 2, localPieceColor);
        placePiecesOnPosition(LOCAL_3, 3, localPieceColor);
        placePiecesOnPosition(LOCAL_5, 5, localPieceColor);
        placePiecesOnPosition(LOCAL_5_HOME, 5, localPieceColor);
        placePiecesOnPosition(REMOTE_2, 1, remotePieceColor);
        placePiecesOnPosition(3, 1, remotePieceColor);
        placePiecesOnPosition(4, 1, remotePieceColor);
        placePiecesOnPosition(5, 1, remotePieceColor);
        placePiecesOnPosition(6, 1, remotePieceColor);
        //end blottable test
    }

    private void testRemoveFromBar() {
        initialSetup();
        //testBlockRemoteHome();
        placePiecesOnPosition(BAR_POS_L, 2, localPieceColor);
        logic.localPlayer.setPiecesOnBar(true);
    }

    private void testBlockRemoteHome() {
        placePiecesOnPosition(25, 2, remotePieceColor);
        placePiecesOnPosition(24, 2, remotePieceColor);
        placePiecesOnPosition(23, 2, remotePieceColor);
        placePiecesOnPosition(22, 2, remotePieceColor);
        placePiecesOnPosition(21, 2, remotePieceColor);
    }

    /**
     * <p>Places pieces on a position given the following parameters.</p>
     * @param positionNum the array number of the position to place pieces on.
     * @param pieceNum the number of pieces to place on this position.
     * @param color the color of the pieces to place on this position.
     */
    private void placePiecesOnPosition(final int    positionNum,
                                       final int    pieceNum,
                                       final Color  color) {
        ArrayList<Piece> pieces = new ArrayList(PIECES_PER_POS);
        for (int i = 0; i < pieceNum; i++) {
            pieces.add(new Piece(color, positionNum));
        }
        positions[positionNum].setPieces(pieces);
    }

    private void addPolygonsToPositions() {
        Polygon[] polygons = new Polygon[28];
        int length = polygons.length;
        for (int i = 0; i < length; i++) {
            polygons[i] = new Polygon();
        }

        //initialize all position polygons
        int top = 0;
        int midTop = 220;
        int barTop = 230;
        int barBottom = 270;
        int midBottom = 280;
        int bottom = 500;

        //initialize top of board polygons
        int Tleft = 0;
        int Tmiddle = 25;
        int Tright = 50;

        for (int i = 14; i < length; i++) {
            if (i > 25) { //home or bar positions
                polygons[i].addPoint(Tleft, top);
                polygons[i].addPoint(Tright, top);
                polygons[i].addPoint(Tright, barTop);
                polygons[i].addPoint(Tleft, barTop);
            } else { //regular positions
                polygons[i].addPoint(Tleft, top);
                polygons[i].addPoint(Tright, top);
                polygons[i].addPoint(Tmiddle, midTop);
            }

            if (i == 19) { //skip over where the bar is to be drawn
                Tleft += 100;
                Tmiddle += 100;
                Tright += 100;
            } else if (i == 25) { //next one is the bar position
                Tleft -= 300;
                Tmiddle -= 300;
                Tright -= 300;
            } else if (i == 26) { //just drew bar, go draw home
                Tleft += 350;
                Tmiddle += 350;
                Tright += 350;
            } else { //shift for next position
                Tleft += 50;
                Tmiddle += 50;
                Tright += 50;
            }
        }

        //initialize bottom of board polygons
        Tleft = 0;
        Tmiddle = 25;
        Tright = 50;

        for (int i = 13; i >= 0; i--) {
            if (i < 2) { //home or bar positions
                polygons[i].addPoint(Tleft, bottom);
                polygons[i].addPoint(Tright, bottom);
                polygons[i].addPoint(Tright, barBottom);
                polygons[i].addPoint(Tleft, barBottom);
            } else { //regular positions
                polygons[i].addPoint(Tleft, bottom);
                polygons[i].addPoint(Tright, bottom);
                polygons[i].addPoint(Tmiddle, midBottom);
            }

            if (i == 8) { //skip over where the bar is to be drawn
                Tleft += 100;
                Tmiddle += 100;
                Tright += 100;
            } else if (i == 2) { //next one is the bar position
                Tleft -= 300;
                Tmiddle -= 300;
                Tright -= 300;
            } else if (i == 1) { //just drew bar, go draw home
                Tleft += 350;
                Tmiddle += 350;
                Tright += 350;
            } else { //shift for next position
                Tleft += 50;
                Tmiddle += 50;
                Tright += 50;
            }
        }

        for (int i = 0; i < positions.length; i++) {
            positions[i].setPolygon(polygons[i]);
        }
    }

    public final void setPieceOnPosition(final int   index,
                                         final Piece piece) {
        positions[index].addPiece(piece);
    }

    /**
     * <p>.</p>
     * @param e mouse event
     */
    public void mouseClicked(final MouseEvent e) {
    }

    /**
     * <p>.</p>
     * @param e mouse event
     */
    public final void mousePressed(final MouseEvent e) {
        Point p = e.getPoint();
        if (selectedPiece == null) {
            //System.out.println("SELECTING PIECE");
            for (Position position : positions) {
                if (position.mouseIsIn(p)) {
                    if (logic.positionHasMoves(position)) {
                        selectedPiece = position.removePiece();
                        if (selectedPiece != null) {
                            System.out.println("\nSELECTED PIECE SET SUCCESSFULLY");
                        } else {
                            System.out.println("YOU FAIL AT PROGRAMMING");
                        }
                        selectedPiece.setPoint(p);
                        break;
                    }
                }
            }
        } else {
            System.out.println("PLACING PIECE");
            for (Position position : positions) {
                if (position.mouseIsIn(p)) {
                    if (logic.canMoveToPosition(position)) {
                        ArrayList<Move> moves = logic.getValidMoves();
                        for (Move move : moves) {
                            if (position.getArrayIndex() == move.getToPosition()) {
                                System.out.println("\nMove: " + move);
                                logic.completeMove(move, position);
                                break;
                            }
                        }
                    }
                }
            }
            System.out.println("CALLING PROCESS MOVES FOR ROLL");
            logic.processMovesForRoll();
        }
        repaint();
    }

    /**
     * <p>.</p>
     * @param e mouse event
     */
    public final void mouseReleased(final MouseEvent e) {
        Point p = e.getPoint();
        if (selectedPiece != null) {
            System.out.println("INSIDE MOUSE RELEASED");
            for (Position position : positions) {
                if (position.mouseIsIn(p)) {
                    if (selectedPiece.getArrayIndex() != position.getArrayIndex()) {
                        ArrayList<Move> moves = logic.getValidMoves();
                        for (Move move : moves) {
                            if (position.getArrayIndex() == move.getToPosition()) {
                                System.out.println("\nMove: " + move);
                                logic.completeMove(move, position);
                                break;
                            }
                        }
                    }
                }
            }
//            System.out.println("CALLING PROCESS MOVES FOR ROLL");
//            logic.processMovesForRoll();
        }
        repaint();
    }

    /**
     * <p>.</p>
     * @param e mouse event
     */
    public void mouseEntered(final MouseEvent e) {
    }

    /**
     * <p>.</p>
     * @param e mouse event
     */
    public void mouseExited(final MouseEvent e) {
    }

    /**
     * <p>.</p>
     * @param e mouse event
     */
    public final void mouseDragged(final MouseEvent e) {
        Point p = e.getPoint();
        if (selectedPiece != null) {
            selectedPiece.setPoint(p);
        }
        repaint();
    }

    /**
     * <p>.</p>
     * @param e mouse event
     */
    public final void mouseMoved(final MouseEvent e) {
        Point p = e.getPoint();
//        for (Position position : positions) {
//            if (position.mouseIsIn(p)) {
//                positionIndex = "Position index: " + position.getArrayIndex();
//                break;
//            }
//        }
        if (selectedPiece != null) {
            selectedPiece.setPoint(p);
        }
        repaint();
    }
}
